#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import csv


def detect_data_type(value):
    """
    检测单元格数据的类型：
    - 如果是数字，返回 'number'；
    - 如果是字符串（非空），返回 'string'；
    - 如果为空（忽略大小写的NULL或空白字符），返回 'empty'。
    """
    if value.strip().lower() in {"", "null"}:
        return "empty"
    try:
        float(value)  # 尝试将值转换为数字
        return "number"
    except ValueError:
        return "string"


def count_rows_exclude_empty(file_path):
    """
    计算 CSV 文件的行数，排除空行和表头。
    """
    with open(file_path, 'r', encoding='utf-8') as csv_file:
        # 使用 enumerate 跳过表头，并排除空行
        total_rows = sum(1 for line_number, line in enumerate(csv_file, start=1) if line.strip() and line_number > 1)
    return total_rows


def validate_csv_structure(file_path, ignore_mixed_types=False):
    """
    校验 CSV 文件是否有异常数据：
    - 检查空行。
    - 检查列中是否存在混合数据类型问题（可配置是否忽略数字与字符串混合问题）。
    - 最后输出所有有异常的列名，并输出数据行数（不包括头部）。
    """
    try:
        # 计算文件总行数（包括表头）
        total_lines = sum(1 for line in open(file_path, 'r', encoding='utf-8'))

        with open(file_path, 'r', encoding='utf-8') as csv_file:
            reader = csv.reader(csv_file)
            header = next(reader, None)  # 获取表头
            if header is None:
                print("CSV 文件为空。")
                return

            column_count = len(header)  # 获取表头列数
            column_data_types = [{} for _ in range(column_count)]  # 每列记录数据类型
            problematic_columns = set()  # 记录异常列的索引
            ignored_columns = set()  # 记录被忽略的列索引
            total_rows = count_rows_exclude_empty(file_path)  # 获取数据行数（不包括表头）
            exception_rows = 0  # 用于统计异常行数
            row_count_during_traversal = 0  # 遍历时累加的行数

            # 用于存储遍历过程中非空行的内容（仅记录异常行）
            exception_rows_data = []

            for row_number, row in enumerate(reader, start=2):  # 从第2行开始读取（第1行为表头）
                row_count_during_traversal += 1  # 每读取一行，行数加1

                # 检查空行
                if not any(cell.strip() for cell in row):  # 如果整行都为空
                    exception_rows += 1
                    exception_rows_data.append((row_number, row))  # 记录异常行
                    continue

                # 检查列数一致性
                if len(row) != column_count:
                    exception_rows += 1
                    exception_rows_data.append((row_number, row))  # 记录异常行
                    continue

                # 检查每列的数据类型
                for col_index, value in enumerate(row):
                    data_type = detect_data_type(value)
                    if data_type != "empty":  # 忽略空值
                        if data_type not in column_data_types[col_index]:
                            column_data_types[col_index][data_type] = True
                        if len(column_data_types[col_index]) > 1:
                            # 检查是否需要忽略类型混合问题
                            current_types = list(column_data_types[col_index].keys())
                            if ignore_mixed_types and set(current_types) <= {"number", "string"}:
                                ignored_columns.add(col_index)
                                continue
                            # 标记该列为异常
                            problematic_columns.add(col_index)
                            exception_rows += 1
                            exception_rows_data.append((row_number, row))  # 记录异常行

            # 输出所有异常列名
            print(f"\n数据行数（不包括头部）: {total_rows}")
            print(f"异常类型行数: {exception_rows}")
            print(f"实际遍历时累加的行数: {row_count_during_traversal}")
            print("\n异常列名列表：")
            for col_index in sorted(problematic_columns):
                print(f"- {header[col_index]} (未忽略)")
            for col_index in sorted(ignored_columns - problematic_columns):
                print(f"- {header[col_index]} (已忽略)")

            # 打印前10个异常行内容
            print("\n前10个异常行数据：")
            for row in exception_rows_data[:10]:
                print(f"第 {row[0]} 行：{row[1]}")

    except Exception as e:
        print(f"文件读取或处理时发生错误: {e}")


def main():
    """
    主方法：用于执行 CSV 文件校验。
    """
    file_path = input("请输入要校验的 CSV 文件路径：").strip()
    ignore_mixed_types = input("是否忽略字符串和数字混合类型问题？(y/n): ").strip().lower() == "y"
    validate_csv_structure(file_path, ignore_mixed_types)


if __name__ == "__main__":
    main()
